// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rainbowchat.im;

import java.io.IOException;
import java.io.InputStream;

import com.eva.framework.utils.LoggerFactory;
import com.x52im.rainbowchat.BaseConf;

import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import net.x52im.mobileimsdk.server.ServerLauncher;
import net.x52im.mobileimsdk.server.event.MessageQoSEventListenerS2C;
import net.x52im.mobileimsdk.server.event.ServerEventListener;
import net.x52im.mobileimsdk.server.network.Gateway;
import net.x52im.mobileimsdk.server.network.GatewayTCP;
import net.x52im.mobileimsdk.server.network.GatewayUDP;
import net.x52im.mobileimsdk.server.processor.BridgeProcessor;
import net.x52im.mobileimsdk.server.qos.QoS4ReciveDaemonC2S;
import net.x52im.mobileimsdk.server.qos.QoS4SendDaemonS2C;
import net.x52im.mobileimsdk.server.utils.ServerToolKits;
import net.x52im.mobileimsdk.server.utils.ServerToolKits.SenseModeTCP;

/**
 * IM服务启动入口类。
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.0
 */
public class ChatServerLauncher extends ServerLauncher
{
	private static ChatServerLauncher instance = null;
	
	public static ChatServerLauncher getInstance() throws IOException
	{
		if(instance == null)
		{
			// 设置UDP网络监听端口（如果需修改此端口号，请前往配置文件中修改）
			GatewayUDP.PORT = BaseConf.getInstance().getIM_SERVER_UDP_LISTENNING_PORT();
			// 设置TCP网络监听端口（如果需修改此端口号，请前往配置文件中修改）
			GatewayTCP.PORT = BaseConf.getInstance().getIM_SERVER_TCP_LISTENNING_PORT();
			
			// 设置服务端支持UDP、TCP协议情况（默认同时支持两种，如需修改请前往配置文件中修改）
			boolean isSupportUDP = BaseConf.getInstance().getIM_SERVER_UDP_SUPPORT();
			boolean isSupportTCP = BaseConf.getInstance().getIM_SERVER_TCP_SUPPORT();
			ServerLauncher.supportedGateways = (isSupportUDP? Gateway.SOCKET_TYPE_UDP:0) | (isSupportTCP? Gateway.SOCKET_TYPE_TCP:0);
						
			instance = new ChatServerLauncher();

			// 开启或关闭Debug日志输出
			QoS4SendDaemonS2C.getInstance().setDebugable(true);
			QoS4ReciveDaemonC2S.getInstance().setDebugable(false);
			
			// 与客户端协商一致的心跳频率模式设置
//			ServerToolKits.setSenseModeUDP(SenseModeUDP.MODE_15S);
			ServerToolKits.setSenseModeTCP(SenseModeTCP.MODE_5S);

			// 设置最大TCP帧内容长度（不设置则默认最大是 6 * 1024字节）
//			GatewayTCP.TCP_FRAME_MAX_BODY_LENGTH = 60 * 1024;

			// 开启TCP协议的SSL/TLS加密传输（请确保客户端也已开发SSL）
//			GatewayTCP.sslContext = createSslContext();
						
			// 关闭与Web端的消息互通桥接器 (TODO：如需与Web端IM互通，请配置此项为ture，否则请设为false)
			ServerLauncher.bridgeEnabled = BaseConf.getInstance().getIM_BRIDGE_ENABLED();
			// 跨服桥接器MQ的URI（本参数只在ServerLauncher.bridgeEnabled为true时有意义）
			BridgeProcessor.IMMQ_URI = BaseConf.getInstance().getIM_BRIDGE_MQ_URI();
		}
		return instance; 
	}
	
	public ChatServerLauncher() throws IOException
	{
		super();
	}
	
    /**
     * 初始化回调处理事件监听器。
     * <p>
     * 本类重写 {@link #initListeners()}方法，以实现应用层的回调监听器以便实现自定义业务
     * 逻辑，可以设置的回调监听器有： {@link #setServerEventListener(ServerEventListener)}
     * 和 {@link #setServerMessageQoSEventListener(MessageQoSEventListenerS2C)}。
     */
	@Override
    protected void initListeners()
    {
    	// ** 设置回调
		this.setServerEventListener(ChatServerEventListener.getInstance());
		this.setServerMessageQoSEventListener(new MessageQoSEventListner());
    }
	
	/**
	 * 重写父类的本方法，方便直接使用本工程中的ChatServerEventListener（即ServerEventListener
	 * 接口的实现类。
	 * 
	 * {@inheritDoc}
	 */
	@Override
	public ChatServerEventListener getServerEventListener()
	{
		return (ChatServerEventListener)serverCoreHandler.getServerEventListener();
	}

	/**
	 * 创建SslContext对象，用于开启SSL/TLS加密传输。
	 *
	 * @return 如果成功创建则返回SslContext对象，否则返回null
	 */
	private static SslContext createSslContext()
	{
		try {
			/** 示例 1：使用证书（证书位于绝对路径）*/
//			// 证书文件
//			File certChainFile = new File("c:/certs/netty-cert2.crt");
//			// 证书文件
//			File keyFile = new File("c:/certs/netty-key2.pk8");
//			// 私钥密码（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
//			String keyPassword = "123456";
//			// 生成SslContext对象（为了方便理解，此处使用的是单向认证）
//			SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();

			/** 示例 2：使用证书（证书位于相对路径）*/
			// TODO: 注意：请使用自已的证书，Demo中带的证书为自签名证书且已绑定域名，不安全！！！
			// 证书文件
			InputStream certChainFile = ChatServerLauncher.class.getResourceAsStream("certs/netty-cert2.crt");
			// 私钥文件（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
			InputStream keyFile = ChatServerLauncher.class.getResourceAsStream("certs/netty-key2.pk8");
			// 私钥密码（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
			String keyPassword = "123456";
			// 生成SslContext对象（为了方便理解，此处使用的是单向认证）
			SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();

			/** 示例 3：使用Netty自带的自签名证书（建议该证书仅用于测试使用）*/
//			SelfSignedCertificate ssc = new SelfSignedCertificate();
//			SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();

			return sslCtx;
		} catch (Exception e) {
			LoggerFactory.getLog().warn("createSslContext()时出错了，原因："+e.getMessage(), e);
		}

		return null;
	}
	
//	// 作为独立的程序启动演示代码
//    public static void main(String[] args) throws Exception 
//    {
//    	// 实例化后记得startup哦，单独startup()的目的是让调用者可以延迟决定何时真正启动IM服务
//    	final ChatServerLauncher sli = new ChatServerLauncher();
//    	
//    	// 启动RainbowChat的socket长连接服务端
//    	sli.startup();
//    	
//    	// 加一个钩子，确保在JVM退出时释放netty的资源
//    	Runtime.getRuntime().addShutdownHook(new Thread() {
//    		@Override
//    		public void run() {
//    			sli.shutdown();
//    		}
//    	});
//    }
}
